﻿using AMS.Services.DomainModels;
using AMS.Services.Job;
using DynamicAuthorization.Sdk.Utils;
using OA.Infrastructure.Abstractions;
using OA.Infrastructure.DomainModels.TemporaryAuthorization;
using OA.Infrastructure.Impl;
using OA.Infrastructure.SignalR.Abstractions;
using OA.Infrastructure.WeCom;
using OA.Infrastructure.WeCom.Generator;
using Quartz;

namespace AMS.Services.Impl;

internal class PermissionTemporaryAuthorizationService : ITemporaryAuthorizationService<TemporaryAuthorizationPermissionModel>
{
    private readonly DbContext _context;

    private readonly TemporaryAuthorizationToolChains<TemporaryAuthorizationPermissionModel> _toolChains;

    public PermissionTemporaryAuthorizationService(
        DbContext context,
        IWeComService weComService,
        ISignalRService signalRService,
        ISchedulerFactory schedulerFactory)
    {
        _context = context;

        _toolChains = new(
            context,
            signalRService,
            weComService,
            schedulerFactory,
            ITemporaryAuthorizationService<TemporaryAuthorizationPermissionModel>.s_request,
            "PermissionTemporaryAuthorization",
            "PermissionTemporaryAuthorizationCallback");
    }

    public async Task<string> TryAcquireAsync(TemporaryAuthorizationPermissionModel model, CancellationToken cancellationToken)
    {
        var @operator = _context.CurrentUser();
        var key = Guid.NewGuid().ToString("N");

        model.Commiter = @operator;

        if (App.ManagedByDocker)
        {
            if (await _toolChains.CanSendMessage(cancellationToken))
            {
                var card = _toolChains.DefaultTemplateCardEngine(model.Commiter)
                    .AddHoriztontalContentItemForText("详情", $"申请临时权限：{model.RequireContent}")
                    .AddHoriztontalContentItemForText("时效", $"{model.Step} {ConvertToTimeUnitName(model.Unit)}")
                    .AddButtonForCallback("驳回", WeComCallbackEvents.TemplateCardPermissionTemporaryAuthRejectEventKey, WeComTemplateCardButtonStyle.Dangerous)
                    .AddButtonForCallback("授权", WeComCallbackEvents.TemplateCardPermissionTemporaryAuthGrantEventKey, WeComTemplateCardButtonStyle.Primary)
                    .Generate();

                await _toolChains.SendCardToWeComAsync(card, key);
            }
        }

        _toolChains.TryAdd(key, model);

        await _toolChains.SendToApproverAsync(new TemporaryAuthorizationRecord
        {
            Commiter = @operator.Name,
            Description = model.RequireContent,
            Prescription = $"{model.Step} {ConvertToTimeUnitName(model.Unit)}",
            Key = key
        }, cancellationToken);

        return key;
    }

    public bool TryConsume(string key, string code, out TemporaryAuthorizationPermissionModel model)
    {
        throw new NotImplementedException();
    }

    public async Task<OperationalResult> GrantAsync(string key, CancellationToken cancellationToken)
    {
        if (_toolChains.TryExtractRequest(key, out var model))
        {
            var tempRole = new Role
            {
                RoleCode = Guid.NewGuid().ToString().Replace("-", string.Empty),
                Name = $"temp-{key}",
                Types = model.ApiPermissions.FromApiPermissionInfo().ToList(),
                ViewRoutes = model.ApiPermissions.AnalyzeViewRoutes()
            };

            await _context.Set<Role>().AddAsync(tempRole, cancellationToken);

            model.Commiter.Roles.Add(tempRole);
            _context.Set<User>().Update(model.Commiter);

            var result = await _context.SetTracking<DbContext>(false).SaveChangesAsync(cancellationToken);

            if (result == 0)
            {
                return OperationalResult.Fail();
            }

            model.GrantDate = DateTime.UtcNow;

            await _toolChains.ScheduleJob<CleanTemporaryAuthorizationRoleJob>(
                tempRole.RoleCode,
                new JobDataMap()
                {
                    { "Id", tempRole.Id },
                    { "Commiter", model.Commiter.Account },
                    { "UserId", model.Commiter.UserId },
                    { "RequireContent", model.RequireContent }
                },
                model.Expiration ?? DateTime.UtcNow,
                "临时授权角色清理",
                cancellationToken);

            await _toolChains.GrantAsync(model, new TemporaryAuthorizationCallbackRecord
            {
                Status = OperationalResult.SUCCESS,
                Description = $"您申请的临时权限（{model.RequireContent}）已通过"
            }, cancellationToken);

            _toolChains.TryRemove(key, out _);

            return OperationalResult.Ok();
        }

        return OperationalResult.Fail();
    }

    public async Task<OperationalResult> RejectAsync(string key, CancellationToken cancellationToken)
    {
        if (_toolChains.TryExtractRequest(key, out var model))
        {
            await _toolChains.RejectAsync(key, model, new TemporaryAuthorizationCallbackRecord
            {
                Status = OperationalResult.FAILURE,
                Description = $"您申请的临时权限（{model.RequireContent}）被驳回"
            }, cancellationToken);

            return OperationalResult.Ok();
        }

        return OperationalResult.Fail();
    }

    private static string ConvertToTimeUnitName(TimeUnit timeUnit)
    {
        return timeUnit switch
        {
            TimeUnit.Minutes => "分钟",
            TimeUnit.Hours => "小时",
            TimeUnit.Days => "天",
            _ => throw new NotImplementedException(),
        };
    }
}